package br.edu.ufcg.greengrid.workload;

import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import br.edu.ufcg.greengrid.AbstractTest;

import com.mallardsoft.tuple.Tuple;

public class PlainTextWorkloadParserTest extends AbstractTest {

	private BulkDataForTests bd;
	private PlainTextConverter c;
	private PlainTextWorkloadParser wp;
	private File fileMachine1;
	private File fileMachine2;

	private List<File> tempFiles;
	
	@Before
	public void setUp() throws Exception {

		this.bd = new BulkDataForTests();
		this.c = new ConverterForTests(this.bd);
		this.wp = new PlainTextWorkloadParser(this.c);
		this.tempFiles = new LinkedList<File>();

		this.fileMachine1 = new File("testResources/dgta/machine1.dat");
		this.fileMachine2 = new File("testResources/dgta/machine2.dat");
		
		assertTrue(fileMachine1.exists());
		assertTrue(fileMachine2.exists());
	}

	@After
	public void tearDown() {
		for (File file : this.tempFiles) {
			assertTrue(file.delete());
		}
	}
	
	@SuppressWarnings("serial")
	@Test
	public void testParse() throws IOException {

		this.wp.parse(new ArrayList<String>() {
			{
				add(fileMachine1.getAbsolutePath());
				add(fileMachine2.getAbsolutePath());
			}
		});

		int counter = 0;
		List<String> expected = getContentFromFiles(this.fileMachine1);
		expected.add(String.valueOf(counter++));
		expected.addAll(getContentFromFiles(this.fileMachine2));
		expected.add(String.valueOf(counter++));
		
		List<String> actual = this.bd.getLines();

		assertIsEqual(expected, actual);
	}

	@SuppressWarnings("serial")
	@Test
	public void testGenerateOutput() throws IOException {

		this.wp.parse(new ArrayList<String>() {
			{
				add(fileMachine1.getAbsolutePath());
				add(fileMachine2.getAbsolutePath());
			}
		});

		File outputFile = createTempFile();
		assertTrue(outputFile.delete());
		this.wp.generateOutput(outputFile, this.bd.getLines());
		
		int counter = 0;
		List<String> expected = getContentFromFiles(this.fileMachine1);
		expected.add(String.valueOf(counter++));
		expected.addAll(getContentFromFiles(this.fileMachine2));
		expected.add(String.valueOf(counter++));
		
		List<String> actual = getContentFromFiles(outputFile);
		
		assertIsEqual(String.format("%s", outputFile.getAbsolutePath()), expected, actual);
		
	}

	private File createTempFile() throws IOException {
		File tempFile = File.createTempFile("test", "temp");
		this.tempFiles.add(tempFile);
		return tempFile;
	}

	private List<String> getContentFromFiles(File... files) throws IOException {

		BufferedReader br = null;
		List<String> lines = new LinkedList<String>();
		String line = null;

		for (File file : files) {

			try {
				br = new BufferedReader(new FileReader(file));

				while ((line = br.readLine()) != null) {
					lines.add(line);
				}

			} finally {
				if (br != null) {
					br.close();
				}
			}

		}

		return lines;
	}

	public static class ConverterForTests implements PlainTextConverter {

		private BulkDataForTests bulkData;
		private int methodClosedWasCalledCounter;
		
		public ConverterForTests(BulkDataForTests bulkData){
			this.bulkData = bulkData;
			this.methodClosedWasCalledCounter = 0;
		}
		
		@Override
		public void close() {

			bulkData.add(String.valueOf(this.methodClosedWasCalledCounter++));
		}

		@Override
		public void convert(String fileName, String line) {

			bulkData.add(line);
		}

		@Override
		public List<String> getLines() {

			return this.bulkData.getLines();
		}

	}
	
	@SuppressWarnings("unchecked")
	public static class BulkDataForTests implements BulkData {

		private List<String> lines;
		
		private BulkDataForTests(){
			this.lines = new LinkedList<String>();
		}
		
		@Override
		public void add(Tuple tuple) {
			add(tuple.toString("", "\t", ""));
		}
		
		public void add(String line) {
			this.lines.add(line);
		}

		@Override
		public List<String> getLines() {
			
			return this.lines;
		}

	}

}
